use actix_web::{actix::Handler, error, Error};
use chrono::Utc;
use common::common::Claims;
use common::db::ConnDsl;
use diesel::{self, ExpressionMethods, QueryDsl, RunQueryDsl, sql_query};
use jwt::{Algorithm, encode, Header};
use md5;
use model::member::{Login, Member, NewMember, Register};
use network::response::{LoginMsgs, Msgs, UserInfoMsgs};
use network::response::MyError;

impl Handler<Register> for ConnDsl {
    type Result = Result<Msgs, Error>;

    fn handle(&mut self, signup_user: Register, _: &mut Self::Context) -> Self::Result {
        if &signup_user.password == &signup_user.confirm_password {
            use common::schema::member::dsl::*;
            let digest = md5::compute(signup_user.password);
            let hash_password = format!("{:x}", digest);

            let new_user = NewMember {
                email: &signup_user.email,
                username: &signup_user.username,
                password: &hash_password,
                created: Utc::now().naive_utc(),
                updated: Utc::now().naive_utc(),
            };
            let conn = &self.0.get().map_err(error::ErrorInternalServerError)?;
            diesel::insert_into(member).values(&new_user).execute(conn).map_err(error::ErrorInternalServerError)?;
            Ok(Msgs {
                status: 200,
                message: "Successful Signup.".to_string(),
            })
        } else {
            Ok(Msgs {
                status: 400,
                message: "failed Signup.".to_string(),
            })
        }
    }
}

impl Handler<Login> for ConnDsl {
    type Result = Result<LoginMsgs, Error>;

    fn handle(&mut self, login: Login, _: &mut Self::Context) -> Self::Result {
        use common::schema::member::dsl::*;
        let conn = &self.0.get().map_err(error::ErrorInternalServerError)?;
        let login_user = member.filter(&username.eq(&login.username)).load::<Member>(conn).map_err(error::ErrorInternalServerError)?.pop();

        let loginPassword = md5::compute(login.password);
        let hash_password = format!("{:x}", loginPassword);
        let no_user = Member::new();
        match login_user {
            Some(login_user) => {
                if (&hash_password == &login_user.password) {
                    let key = "secret";
                    let claims = Claims {
                        user_id: login_user.id.to_string(),
                    };
                    let token = match encode(&Header::default(), &claims, key.as_ref()) {
                        Ok(t) => t,
                        Err(_) => panic!() // in practice you would return the error
                    };
                    let the_user = Member {
                        id: login_user.id,
                        email: login_user.email.clone(),
                        username: login_user.username.clone(),
                        password: login_user.password.clone(),
                        avatar: login_user.avatar.clone(),
                        mobile: login_user.avatar.clone(),
                        created: login_user.created.clone(),
                        updated: login_user.updated.clone(),
                    };
                    Ok(LoginMsgs {
                        status: 200,
                        token: token,
                        signin_user: the_user,
                        message: "Succesfully signin.".to_string(),
                    })
                } else {
                    Msgs {
                        status: 400,
                        message: "failed login.".to_string(),
                    }
                }
            }
            None => {
                Ok(LoginMsgs {
                    status: 400,
                    token: "".to_owned(),
                    signin_user: no_user,
                    message: "Signin failure.".to_string(),
                })
            }
        }
    }
}